<!--
 * @Author: liaoxing
 * @Date: 2021-10-18 15:46:52
 * @LastEditors: liaoxing
 * @LastEditTime: 2021-11-04 14:12:19
 * @Description: liaoxing created
 * @FilePath: \pin-xin-admin\src\views\process\bpmn\index.vue
-->
<template>
  <div class="createProcess">
    <!-- 操作按钮 -->
    <div class="btnBox">
      <el-row>
        <el-button size="mini" type="primary" @click="toBefore">前进</el-button>
        <el-button size="mini" type="primary" @click="toAfter">后退</el-button>
        <el-button size="mini" type="primary" @click="clearBoard">清空</el-button>
        <el-upload
          class="uploadBox"
          :show-file-list="false"
          :on-change="handleOnchangeFile"
          :auto-upload="false"
          action
        >
          <el-button size="mini" type="primary">导入</el-button>
        </el-upload>
        <el-button size="mini" type="primary" @click="downLoad">下载</el-button>
        <el-button size="mini" type="primary" @click="saveBoard">保存</el-button>
      </el-row>
    </div>
    <!-- 流程画板 -->
    <div class="processBoard" ref="processBoard"></div>
    <!-- 节点详情面板 -->
    <div class="nodeInfoBox" :style="{width: nodeInfoBoxWidth + 'px'}">
      <span class="icon" @click="changeNodeInfoBoard">
        <i :class="nodeInfoBoxWidth === 400 ? 'el-icon-caret-right' : 'el-icon-caret-left'"></i>
      </span>
      <el-form v-if="inputData.id" :model="inputData" label-width="100px">
        <el-form-item label="流程id">
          <el-input :disabled="true" v-model="inputData.id" placeholder=""></el-input>
        </el-form-item>
        <el-form-item label="名称">
          <el-input v-model="inputData.name" placeholder=""></el-input>
        </el-form-item>
        <el-form-item label="创建人" v-if="inputData.initiator">
          <el-input v-model="inputData.initiator" placeholder=""></el-input>
        </el-form-item>
        <el-form-item label="负责人" v-if="inputData.assignee">
          <el-input v-model="inputData.assignee" placeholder=""></el-input>
        </el-form-item>
        <!-- <el-form-item label="流转条件" v-if="currentNode === 'bpmn:SequenceFlow'">
          <el-input v-model="inputData.condition" placeholder=""></el-input>
        </el-form-item> -->
        <!-- <el-form-item label="创建时间">
          <el-input v-model="inputData.createTime" placeholder=""></el-input>
        </el-form-item>
        <el-form-item label="数组">
          <template v-if="inputData.list.length">
            <div v-for="(item, index) in inputData.list" :key="'list' + index">
              <el-input style="margin-top: 10px" v-model="item.name" placeholder></el-input>
              <el-input style="margin-top: 10px" v-model="item.data" placeholder></el-input>
            </div>
          </template>
          <el-button size="mini" type="primary" @click="addListData">新增</el-button>
        </el-form-item>
        <template v-if="inputData.obj">
          <el-form-item label="对象">
            姓名
            <el-input v-model="inputData.obj.name" placeholder></el-input>
          </el-form-item>
          <el-form-item label="对象">
            数值
            <el-input v-model="inputData.obj.data" placeholder=""></el-input>
          </el-form-item> 
        </template>-->
      </el-form>
    </div>
  </div>
</template>

<script>
import xmlStr from '@/constant/xmlStr'
import BpmnModeler from 'bpmn-js/lib/Modeler'
// import camundaExtension from '@/constant/camunda.json'
import flowableModdleDescriptor from '@/constant/flowable.json'
import debounce from '@/utils/debounce'
import defaultData from '@/constant/nodeDataModule'
import sessionStorage from "@/utils/sessionStorage"
import customTranslate from '@/components/CustomTranslate'
import paletteProvider from '@/constant/bpmnPalette'
import contextPadProvider from '@/constant/bpmnContextPad'
export default {
  name: 'createProcess',
  data() {
    return {
      bpmnModeler: null,
      container: null,
      processBoard: null,
      inputData: {},
      nodeInfoBoxWidth: 0,
      updataXML: null,
      isImportXml: false,
      currentModel: null,
      currentNode: ""
    }
  },
  watch: {
    inputData: {
      deep: true,
      handler: function (val) {
        if (!val.id) return
        this.updataXML()
      },
    },
  },
  created () {
    this.currentModel = JSON.parse(sessionStorage.get('currentModel'))
    // this.getByModelId(modelId)
  },
  mounted () {
    this.updataXML = debounce(this.josnToXML, 100)
    this.createBoard()
  },
  methods: {
    /* 获取模型信息 */
    getByModelId (id) {
      this.$serve.getByModelId(id)
        .then(res => {
          console.log(this.currentModel)
          let xml = xmlStr(this.currentModel.tag, this.currentModel.name)
          if (res.code === 200) {
            xml = res.data.modelXml
          } else {
            if (res.code != 400) {
              this.$message.error("请求异常！" + res.msg)
            }
          }
          this.bpmnModeler.importXML(xml, (err) => {
            if (err) {
              this.$message.error('绘制失败')
            } else {
              this.success()
            }
          })
        })
        .catch(() => {
          this.$message.error("请求失败！" )
        })
    },
    /* 获取远端模型 */
    // getBpmnModel (id) {
    //   this.$serve.getBpmnByModelId(id)
    //     .then(res => {
    //       if (res.code === 200) {
    //         console.log(res)
    //         // this.bpmnModeler.importXML(res.data.modelXml, (err) => {
    //         //   if (err) {
    //         //     this.$message.error('绘制失败')
    //         //   } else {
    //         //     // this.$message.success('绘制成功')
    //         //     this.success()
    //         //   }
    //         // })
    //       } else {
    //           this.$message.error("请求异常！" + res.msg)
    //         }
    //     })
    //     .catch(() => {
    //       this.$message.error("请求失败！" )
    //     })
    // },
    /* 数组新增 */
    addListData() {
      if (!this.inputData.id) return
      this.inputData.list.push({
        name: '',
        data: '',
      })
    },
    /* 展开节点信息 */
    changeNodeInfoBoard() {
      if (this.nodeInfoBoxWidth < 400) {
        this.nodeInfoBoxWidth = 400
      } else {
        this.nodeInfoBoxWidth = 0
      }
    },
    /* 创建流程画板 */
    createBoard() {
      const processBoard = this.$refs.processBoard
      // 建模
      this.bpmnModeler = new BpmnModeler({
        container: processBoard,
        additionalModules: [
            {translate: ['value', customTranslate]},
            paletteProvider,
            contextPadProvider
        ],
        moddleExtensions: {flowable: flowableModdleDescriptor}
      })
      this.createNewDiagram()
    },
    /* 绘制xml */
    createNewDiagram() {
      if (this.currentModel.id) {
        this.getByModelId(this.currentModel.id)
        return
      }
      let xml = xmlStr(this.currentModel.modelKey, this.currentModel.name)
      this.bpmnModeler.importXML(xml, (err) => {
        if (err) {
          console.error(err)
        } else {
          this.success()
        }
      })
    },
    /* 绘制成功创建监听 */
    success() {
      this.addEventBusListener()
      this.addModelerListener()
    },
    /* 获取当前画板信息 */
    getCurrentBoardData() {
      let info = this.bpmnModeler.getDefinitions()
      console.log(info)
    },
    /* 保存 */
    saveBoard() {
      this.bpmnModeler.saveXML({ format: true }, (err, data) => {
        console.log(data)
        this.$serve.saveBpmnModel({
          modelLongId: this.currentModel.id,
          bpmContent: data
        }).then(res => {
          if (res.code === 200) {
            this.$message.success("保存成功！")
          } else {
            this.$message.error("请求异常！" + res.msg)
          }
        })
        .catch(() => {
          this.$message.error("请求失败！" )
        })
      })
    },
    /* 下载 */
    downLoad() {
      this.bpmnModeler.saveXML({ format: true }, (err, data) => {
        const dataTrack = 'bpmn'
        const a = document.createElement('a')
        const name = `diagram.${dataTrack}`
        a.setAttribute(
          'href',
          `data:application/bpmn20-xml;charset=UTF-8,${encodeURIComponent(
            data
          )}`
        )
        a.setAttribute('target', '_blank')
        a.setAttribute('dataTrack', `diagram:download-${dataTrack}`)
        a.setAttribute('download', name)
        document.body.appendChild(a)
        a.click()
        document.body.removeChild(a)
      })
    },
    /* 前进 */
    toBefore() {
      this.bpmnModeler.get('commandStack').redo()
    },
    /* 后退 */
    toAfter() {
      this.bpmnModeler.get('commandStack').undo()
    },
    /* 清空 */
    clearBoard() {
      this.bpmnModeler.clear()
      this.inputData = defaultData()
      this.nodeInfoBoxWidth = 0
      this.bpmnModeler.importXML(xmlStr(this.currentModel.tag, this.currentModel.name), (err) => {
        if (err) {
          this.$message.error('清除失败')
        } else {
          this.$message.info('清除成功')
        }
      })
      // this.bpmnModeler.destroy()
    },
    /* 导入 */
    handleOnchangeFile(file) {
      this.isImportXml = true
      const reader = new FileReader()
      let data = ''
      reader.readAsText(file.raw)
      reader.onload = (event) => {
        data = event.target.result
        this.bpmnModeler.importXML(data, (err) => {
          if (err) {
            this.$message.error('导入失败')
          } else {
            this.$message.success('导入成功')
          }
        })
      }
    },
    /* 添加节点事件监听 */
    addEventBusListener() {
      const eventBus = this.bpmnModeler.get('eventBus')
      const eventType = ['element.click', 'element.changed']
      eventType.forEach(type => {
        eventBus.on(type, e => {
          console.log(type, e.element.type)
          /* lable值改变触发 */
          if (type === "element.changed") {
            if (e.element.type === "label") {
              this.inputData.name = e.element.businessObject.name
              console.log(this.inputData)
            }
            return
          }
          /* 点击事件 */
          this.currentNode = e.element.businessObject.$type
          if (!e || e.element.type == 'bpmn:Process') {
            this.nodeInfoBoxWidth = 0
            return
          }
          let data = e.element
          this.getNodeInfo(data.businessObject)

          if (this.nodeInfoBoxWidth === 400) return
          this.nodeInfoBoxWidth = 400
        })
      })
    },
    /* 添加模块事件监听 */
    addModelerListener() {
      const events = ['shape.add', 'shape.removed']
      events.forEach(type => {
        this.bpmnModeler.on(type, e => {
          // console.log(type, e)
          if (type == 'shape.add') {
            // console.log('shape.add')
            let tempData = null
            if (this.isImportXml) {
              this.isImportXml = false
              return
            } else {
              tempData = defaultData()
              tempData.id = e.element.id
            }
            // console.log("新增数据")
            this.inputData = tempData
            return
          }
          if (type == 'shape.removed') {
            console.log('shape.removed')
            return
          }
        })
      })
    },
    /* 当前选中节点信息 */
    checkedNodeOInfo() {
      const modeling = this.bpmnModeler.get('modeling')
      modeling.updateLabel(this.currentElementId, '奥特曼打小怪兽')
    },
    /* json转XMl */
    /* josnToXMLxxx () {
      console.log('执行转换')
      if (!this.inputData.id) return
      const elementRegistry = this.bpmnModeler.get('elementRegistry') // 注册实例
      const bpmnFactory = this.bpmnModeler.get('bpmnFactory') // 注册生成器
      const modeling = this.bpmnModeler.get('modeling') // 获取当前操作节点
      const element = elementRegistry.get(this.inputData.id)
      if (!element) return
      // console.log(elementRegistry)
      // console.log(bpmnFactory)
      // console.log(modeling)
      // element.businessObject.initiator = "我是的"
      // console.log(element.businessObject)
      const extensionElements = bpmnFactory.create('bpmn:ExtensionElements')
      const inputOutput = bpmnFactory.create('flowable:InputOutput')
      extensionElements.values = [inputOutput]
      inputOutput.inputParameters = []

      for (let nodeKey in this.inputData) {
        let inputParameter = null
        if (!this.inputData[nodeKey]) {
          // console.log('空的')
          continue
        }
        if ((typeof this.inputData[nodeKey] === 'string' && this.inputData[nodeKey] !== '') || typeof this.inputData[nodeKey] === 'boolean' || typeof this.inputData[nodeKey] === 'number') {
          inputParameter = bpmnFactory.create('flowable:InputParameter', {
            name: nodeKey,
            value:
              typeof this.inputData[nodeKey] === 'string'
                ? this.inputData[nodeKey]
                : JSON.stringify(this.inputData[nodeKey]),
          })
        } else if (typeof this.inputData[nodeKey] === 'object') {
          if (this.inputData[nodeKey] instanceof Array) {
            if (!this.inputData[nodeKey].length) {
              // continue
            }
            inputParameter = bpmnFactory.create('flowable:InputParameter', {name: nodeKey})
            const list = bpmnFactory.create('flowable:List')
            list.items = []
            this.inputData[nodeKey].forEach(item => {
              const itemValue = bpmnFactory.create('flowable:Value', {value: JSON.stringify(item)})
              list.items.push(itemValue)
            })
            inputParameter.definition = list
          } else {
            if (JSON.stringify(this.inputData[nodeKey]) === '{}') continue;
              inputParameter = bpmnFactory.create('flowable:InputParameter', {name: nodeKey});
              const map = bpmnFactory.create('flowable:Map');
              map.entries = [];
              for (const mapKey in this.inputData[nodeKey]) {
                if (this.inputData[nodeKey][mapKey] !== '') {
                  const itemValue = bpmnFactory.create('flowable:Entry', {
                    key: mapKey,
                    value: this.inputData[nodeKey][mapKey]
                  });
                  map.entries.push(itemValue);
                }
                inputParameter.definition = map;
              }
              inputParameter.definition = map
            }
          }
        }
        inputParameter !== null &&
          inputOutput.inputParameters.push(inputParameter)
      modeling.updateProperties(element, {extensionElements})
    }, /*
    /* json转XMl */
    josnToXML () {
      console.log('执行转换')
      if (!this.inputData.id) return
      const elementRegistry = this.bpmnModeler.get('elementRegistry') // 注册实例
      const modeling = this.bpmnModeler.get('modeling') // 获取当前操作节点
      const bpmnFactory = this.bpmnModeler.get('bpmnFactory')
      const element = elementRegistry.get(this.inputData.id)
      
      if (!element) return
      element.businessObject.initiator = this.inputData.initiator
      element.businessObject.assignee = this.inputData.assignee
      element.businessObject.name = this.inputData.name

      let conditionExpression ;
      if (this.inputData.condition) {
        conditionExpression = bpmnFactory.create("bpmn:FormalExpression", {
          body: this.inputData.condition,
        })
      }
      // element.businessObject.condition = this.inputData.condition
      
      modeling.updateProperties(element, {conditionExpression: conditionExpression})
      console.log(element)
    },
    /* 获取节点信息 */
    getNodeInfo (data) {
      console.log(data)
      if (this.inputData && data.id === this.inputData.id) return
        let formData = defaultData()
        formData['id'] = data.id
        formData['name'] = data.name || ''
        formData['initiator'] = data.initiator || ''
        formData['assignee'] = data.assignee || ''
        formData['condition'] = data.conditionExpression ? data.conditionExpression.body : ''
        // let params = this.getExtensionElement(data, 'camunda:InputOutput')
        // this.inputData = data.id
        // console.log(params)
        // if (!params) return  
        // params.inputParameters.forEach((item) => {
        //   let definition = item.definition
        //   if (definition) {
        //     if (definition.$type === 'camunda:List') {
        //       if (!definition.items) return
        //       let arr = []
        //       definition.items.forEach((itemsItem) => {
        //         arr.push(JSON.parse(itemsItem.value))
        //       })
        //       formData[item.name] = arr
        //     } else if (definition.$type === 'camunda:Map') {
        //       let obj = {}
        //       if (definition.entries) {
        //         definition.entries.forEach((entriesItem) => {
        //           obj[entriesItem.key] = entriesItem.value
        //         })
        //         formData[item.name] = obj
        //       }
        //     }
        //   } else {
        //     formData[item.name] = item.value
        //   }
        // })
        console.log(formData)
        this.inputData = formData
    },
    /* 获取节点 */
    getExtensionElement(element, type) {
      if (!element.extensionElements) {
        return
      }
      return element.extensionElements.values.filter((extensionElement) => {
        return extensionElement.$instanceOf(type)
      })[0]
    },
  },
}
</script>

<style lang="less">
.createProcess {
  width: 100%;
  height: calc(100vh - 85px);
  box-sizing: border-box;
  padding: 20px;
  position: relative;
  background-image: linear-gradient(
      90deg,
      rgba(180, 180, 180, 0.15) 10%,
      rgba(0, 0, 0, 0) 10%
    ),
    linear-gradient(rgba(180, 180, 180, 0.15) 10%, rgba(0, 0, 0, 0) 10%);
  background-size: 10px 10px;
  .uploadBox {
    display: inline;
    margin: 0 10px;
  }
  .btnBox {
    position: absolute;
    z-index: 9;
    text-align: center;
    width: 40%;
    top: 20px;
    left: 30%;
  }
  .nodeInfoBox {
    position: absolute;
    z-index: 9;
    height: 100%;
    top: 0;
    right: 0;
    background-color: white;
    box-shadow: -2px 0 2px gainsboro;
    transition: 0.2s linear;
    box-sizing: border-box;
    padding: 15px;
    .icon {
      position: absolute;
      top: 5px;
      left: 5px;
      font-size: 20px;
      cursor: pointer;
    }
  }
  .processBoard {
    width: 100%;
    height: 100%;
  }
  .containers {
    position: absolute;
    background-color: #ffffff;
    width: 100%;
    height: 100%;
  }
  .panel {
    position: absolute;
    right: 0;
    top: 0;
    width: 300px;
  }
  .bjs-powered-by {
    display: none;
  }
}
</style>